# 代理 - 抓取网易云评论信息
# pip install pycrypto
'''
直接安装 Crypto 是不好使的。因为历史原因导致的比较混乱，引用外部博友的解释内容如下：

pycrypto、pycrytodome 和 crypto 是一个东西，crypto 在 python 上面的名字是 pycrypto，它是一个第三方库，但是已经停止更新三年了，所以不建议安装这个库；
这个时候 pycryptodome 就来了，它是 pycrypto 的延伸版本，用法和 pycrypto 是一模一样的；
所以，我现在告诉大家一种解决方法--直接安装：pip install pycryptodome
但是，在使用的时候导入模块是有问题的，这个时候只要修改一个文件夹的名称就可以完美解决这个问题，
Python\Python36\Lib\site-packages，找到这个路径，下面有一个文件夹叫做 crypto,将小写 c 改成大写 C 就 ok 了。
为了安装方便，可以直接使用下面的命令：

    pip install crypto pycryptodome
    pip uninstall crypto pycryptodome
    pip install pycryptodome
而如果你是 linux 环境，则直接安装 pycryptodome 即可：
pip install pycryptodome
'''
import requests
from Crypto.Cipher import AES
from base64 import b64encode
import json


url = 'https://music.163.com/weapi/comment/resource/comments/get?csrf_token=c43aa82833e9db0248ca8cddf2014b33'


# 1. 找到未加密的参数
data = {
    'csrf_token': "c43aa82833e9db0248ca8cddf2014b33",
    'cursor': "-1",
    'offset': "0",
    'orderType': "1",
    'pageNo': "1",
    'pageSize': "20",
    'rid': "R_SO_4_1974443814",
    'threadId': "R_SO_4_1974443814"
}

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67"
}

proxies = {
    "http": "http://88.99.10.254:1080"
}

# 2. 想办法把参数进行加密（必须参考网易的逻辑），params =》 encText，encSecKey =》 encSecKey
# 通过 window.asrsea 找到加密函数
'''
function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)  # 循环16次
            e = Math.random() * b.length,   # 随机数
            e = Math.floor(e),  # 取整
            c += b.charAt(e);   # 取字符串中的某个位置
        return c    # 生成随机的 16 位字符串
    }
    function b(a, b) {      # a 是要加密的内容
        var c = CryptoJS.enc.Utf8.parse(b)      # c 是加密的密钥，所以 b 就是密钥
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)      # e 是数据
          , f = CryptoJS.AES.encrypt(e, c, {        # AES 加密，c 是加密的密钥
            iv: d,      # 偏移量
            mode: CryptoJS.mode.CBC     # 加密模式是 CBC
        });
        return f.toString()
    }
    function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d, a)
    }
    function d(d, e, f, g) {
        var h = {}
          , i = a(16);      #  i 就是 16 位的随机数
        h
        h.encText = b(h.encText, i),    # 返回的就是 params，
        h.encSecKey = c(i, e, f),   # 得到的就是 encSecKey，e 和 f 是定值，如果把 i 也设置成定值，encSecKey  也就成为了定值
        return h.encText = b(d, g)  # 逆推出来 g 是密钥
    }
'''

d = {"rid": "R_SO_4_1974443814", "threadId": "R_SO_4_1974443814", "pageNo": "1", "pageSize": "20", "cursor": "-1",
     "offset": "0", "orderType": "1", "csrf_token": "c43aa82833e9db0248ca8cddf2014b33"}
e = "010001"
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7""00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud"
i = "Pw0F6aFMG6lnuXoJ"      # 手动固定的参数，16 位

# 由于 i 是固定的，所以encSecKey 就是固定的，c() 函数的结果就是固定的
def get_encSecKey():
    return "d8b365048a3402ec663027de5699cc92ea073f0b8089c9325b748b9c325f4cbe986087d8f98a4d32c5c049a9519e2e0e2aad86f83acc5c08af0bd836573f2502c8a8bb4c2787f3b52279d648fa353992de5982e802a7ae24e249f077e1e05f498acff3908f4752c219a5a5950351add287bf70f84d6f01367cc0f63d651595ab"


# 默认这里收到的是字符串
def get_params(data):
    first = enc_params(data, g)
    second = enc_params(first, i)
    return second

def to_16(data):
    pad = 16 - len(data) % 16
    data += chr(pad) * pad
    return data


# 加密过程
def enc_params(data, key):
    iv = '0102030405060708'
    data = to_16(data)
    aes = AES.new(key=key.encode('utf-8'), IV=iv.encode('utf-8'), mode=AES.MODE_CBC)  # 创建加密器
    bs = aes.encrypt(data.encode('utf-8'))  # 加密，加密的内容的长度必须是16的倍数，
    return str(b64encode(bs), 'utf-8')  # 转化成字符串返回


# 3. 请求到网易，拿到评论信息
resp = requests.post(url, data={
                        'params': get_params(json.dumps(data)),
                        'encSecKey': get_encSecKey()
                    },
                     headers=headers,
                     proxies=proxies)
print(resp.text)
resp.close()
